package socketmvc.server;

import org.tio.core.Node;
import org.tio.server.TioServerConfig;
import org.tio.websocket.server.WsServerConfig;
import socketmvc.core.IoServer;
import socketmvc.core.codec.IPacketCodec;
import socketmvc.core.config.IpConfigType;
import socketmvc.core.config.SocketMvcConfig;
import socketmvc.core.context.ConfigContext;
import socketmvc.core.filter.IFilter;
import socketmvc.server.config.BatchIpSocketMvcConfig;
import socketmvc.server.config.SingleSocketMvcConfig;
import socketmvc.server.context.SocketServerContext;
import socketmvc.server.tcp.TcpMsgHandler;
import socketmvc.server.tcp.TcpServer;
import socketmvc.server.ws.WsMsgHandler;
import socketmvc.server.ws.WsServer;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.Predicate;

/**
 * im服务启动类
 * @author xueyanjun
 */
public class SocketServerStarter {

    protected String name;

    protected SocketMvcConfig socketMvcConfig;

    protected SocketServerContext serverContext;

    protected List<TioServerConfig> ioConfigs = new ArrayList<>();

    protected List<IoServer> ioServers = new ArrayList<>();

    public SocketServerStarter(SocketMvcConfig socketMvcConfig) {
        this.socketMvcConfig = socketMvcConfig;
        this.name = "socketmvc-server";
    }

    public SocketServerStarter(String name, SocketMvcConfig socketMvcConfig) {
        this.name = name;
        this.socketMvcConfig = socketMvcConfig;
    }

    public String getName() {
        return name;
    }


    /**
     * 启动xim server
     * @throws Exception ex
     */
    public void start() throws Exception {

        List<? extends SocketMvcConfig.IpConfig> ipConfigs;
        if (socketMvcConfig instanceof BatchIpSocketMvcConfig){
            ipConfigs = ((BatchIpSocketMvcConfig) socketMvcConfig).getIpConfigs();
        }else{
            ipConfigs = List.of(socketMvcConfig.getIpConfig());
        }

        if (ipConfigs == null || ipConfigs.isEmpty()){
            throw new RuntimeException("websocket 和 tcp 服务至少开启一个");
        }

        // 初始化过滤器链条
        Collection<IFilter> filters = socketMvcConfig.getFilters();
        if (filters != null && !filters.isEmpty()){
            socketMvcConfig.getFilterHandler().addFilter(filters);
        }
        // 初始化数据包编解码器
        Collection<IPacketCodec> packetCodecs = socketMvcConfig.getPacketCodecs();
        if (packetCodecs != null && !packetCodecs.isEmpty()){
            socketMvcConfig.getProtocolCodecHandler().addProtocolCodec(packetCodecs);
        }

        // 初始化业务处理器
        try {
            Set<String> userPackages = Optional.ofNullable(socketMvcConfig.getProcessorScanPackage()).orElse(Set.of());
            Set<String> packages = new LinkedHashSet<>(userPackages.size()+1);
            packages.add("socketmvc.core.processor");
            packages.addAll(userPackages);
            socketMvcConfig.getProcessorMethodHandler().init(packages);
        } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }


        WsMsgHandler wsMsgHandler = null;
        TcpMsgHandler tcpMsgHandler = null;
        for (SocketMvcConfig.IpConfig config : ipConfigs) {
            if (!config.isEnable()) {
                continue;
            }
            SingleSocketMvcConfig mvcConfig = new SingleSocketMvcConfig(socketMvcConfig, config);
            SocketServerContext serverContext = this.serverContext = new SocketServerContext(this, null, mvcConfig);
            ConfigContext.setConfig(config,mvcConfig); // 将mvc config存储到配置上下文工具类中
            IpConfigType type = config.getType();
            switch (type) {
                case WEB_SOCKET:
                    // 配置 websocket server
                    if (wsMsgHandler == null) {
                        wsMsgHandler = new WsMsgHandler(serverContext);
                    }
                    WsServerConfig wsServerConfig = new WsServerConfig(config.getPort());
                    wsServerConfig.setBindIp(config.getHost());
                    WsServer wsServer = new WsServer(wsServerConfig, wsMsgHandler);
                    TioServerConfig wsConfig = wsServer.getTioServerConfig();
                    // 设置ssl
                    SocketMvcConfig.SSL wsSsl = config.getSsl();
                    if (wsSsl != null) {
                        wsConfig.useSsl(wsSsl.getKeyStoreFile(), wsSsl.getTrustStoreFile(), wsSsl.getPassword());
                        wsConfig.setName("socketmvc-wss-server:" + config.getPort());
                    } else {
                        wsConfig.setName("socketmvc-ws-server:" + config.getPort());
                    }
                    wsConfig.setHeartbeatTimeout(600000L); // 十分钟没有收发报文则移除连接
                    ioConfigs.add(wsConfig);
                    ioServers.add(new IoServer(wsServer.getTioServer(), new Node(config.getHost(), config.getPort())));
                    serverContext.setIoConfig(wsConfig);
                    break;
                case TCP:
                    if (tcpMsgHandler == null) {
                        tcpMsgHandler = new TcpMsgHandler(serverContext);
                    }
                    // 配置 tcp server
                    TioServerConfig tcpConfig = new TioServerConfig(
                            "socketmvc-tcp-server:" + config.getPort(), tcpMsgHandler, null);
                    // 十分钟没有收发报文则移除连接
                    tcpConfig.setHeartbeatTimeout(600000L);
                    // 设置ssl
                    SocketMvcConfig.SSL tcpSsl = config.getSsl();
                    if (tcpSsl != null) {
                        tcpConfig.useSsl(tcpSsl.getKeyStoreFile(), tcpSsl.getTrustStoreFile(), tcpSsl.getPassword());
                    }
                    ioConfigs.add(tcpConfig);
                    ioServers.add(new IoServer(new TcpServer(tcpConfig), new Node(config.getHost(), config.getPort())));
                    if (serverContext.getIoConfig() == null) {
                        serverContext.setIoConfig(tcpConfig);
                    }
                    break;
                case UDP:
                    break;
            }

        }

        // 多个服务启动时共享配置
        if (ioConfigs.size() > 1){
            TioServerConfig head = ioConfigs.get(0);
            for (int i = 1; i < ioConfigs.size(); i++) {
                TioServerConfig config = ioConfigs.get(i);
                head.share(config);
            }
        }

        if (!ioServers.isEmpty()){
            for (IoServer ioServer : ioServers) {
                ioServer.start();
            }
        }
        SocketMvcGlobal.serverContext = this.serverContext;
    }


    /**
     * 关闭xim server
     */
    public void stop(Predicate<Node> predicate){
        if (!ioServers.isEmpty()){
            for (IoServer ioServer : ioServers) {
                if (predicate.test(ioServer.getServerNode())){
                    ioServer.stop();
                }
            }
        }
        SocketMvcGlobal.serverContext = this.serverContext = null;
    }

    public void stop(){
        stop(node -> true);
    }

    public SocketServerContext getServerContext() {
        return serverContext;
    }
}
